Skip to content

feat: move Programs to its own service (EN-207) - #1290

Merged
carddev81 merged 11 commits into
mainfrom
EN-207-move-programs-to-its-own-repo
Nov 7, 2026
Merged

carddev81 merged 11 commits into
mainfrom
EN-207-move-programs-to-its-own-repo

Conversation

@carddev81

Copy link
Copy Markdown
Contributor

Pre-Submission PR Checklist

  • No debug/console/fmt.Println statements
  • Unnecessary development comments removed
  • All acceptance criteria verified (after staging)
  • Functions according to ticket specifications
  • Tested manually where applicable (make dev end to end still to run, see below)
  • Branch rebased with latest main
  • No business logic exists within the database layer
  • DB migrations (if any) are backward-compatible and online-safe — see Database Migrations in CONTRIBUTING.md

Description of the change

Core stops running the Programs module and calls the new unlocked-programs service instead.

  • Core asks Programs over HTTP with the contracts programs.Client.

    • PROGRAMS_URL defaults to http://programs:8080.
    • The server refuses to start without PROGRAMS_TOKEN, as it does for Open Content.
    • The calls are users per class, transfer conflicts, the facility list and delete check, the usage report, and the dashboard and metrics routes.
  • Transfer, deactivate and bulk deactivate call Programs first, then run core's write.

    • If Programs fails, core changes nothing and answers the same 500 as before.
    • The database layer no longer calls Programs (ProgramsInTx is gone).
  • Provider Platforms' live-provider calls (coreapi.LiveProviders) go through a small adapter to Programs.

  • Programs' routes and ingress:

    • Programs' routes leave core. The ingress, and rev_proxy in dev, send their paths to the service.
    • Five routes moved to Programs-only paths, and the frontend's 7 call sites were updated:
      • /api/users/{id}/programs, /attendance-trend, /attendance-export and /program-completions → /api/program-users/{id}/...
      • /api/facilities/{id}/instructors → /api/program-facilities/{id}/instructors
  • Removed from core:

    • the module, its registry functions and its depguard rule;
    • the module.Deps pieces only Programs used (HTTP, Features, Reports, ModuleDBs) and RunAs;
    • Jasper's GenerateReportPDF and the 5 Programs report templates. Core keeps Jasper and the usage report.
    • the seeder's Programs data (make seed runs Programs' seed-dev instead);
    • excelize and rrule.
  • Tests:

    • A fake Programs is served over real HTTP with the contracts handler, like the fake Open Content.
    • TestProgramsCoreReads passes with its goldens unchanged.
    • 14 goldens for core's own routes moved byte-identical to testdata/programs_core_routes.
    • New tests cover:
      • who may call the dashboard and metrics routes;
      • the order: when Programs fails, transfer and both deactivations stop and core is unchanged.
  • Compose, make and README, like Open Content:

    • x-programs builds from GitHub (PROGRAMS_REF, default main), or from PROGRAMS_DIR.
    • Services: programs-migrate (after server-migrate), programs on host port 8084, and programs-worker.
    • make migrate, migrate-fresh and seed run Programs' migrate, migrate-reset and seed-dev.
  • Migration 00058's Down fixed: its cron_jobs insert had no id, so migrate-fresh failed on main. Up is unchanged, so sites aren't affected.

  • Contracts: unlocked-contracts is pinned to v0.5.0.

  • Related issues: https://unlockedlabs.atlassian.net/browse/EN-207

Screenshot(s)

No UI changes. The frontend only calls the renamed paths.

Additional context

Run:

  • vet on all three Go modules;
  • golangci-lint v2.14.0 (0 issues);
  • race tests in every package;
  • govulncheck (0 affecting);
  • eslint, prettier and tsc -b.

Mutation checks failed the tests as they should:

  • deactivating in the wrong order;
  • deactivating the wrong user;
  • swapped live-provider arguments;
  • a transfer that ignores a Programs failure (this one fails by timeout).

Not verified yet: that a successful transfer sends Programs the right IDs. SQLite's single connection can't run that route. I'll check it in make dev before merge, along with the renamed pages.

Watch:

  • Order of transfer and deactivation (Cory's question on unlocked-contracts#8). Programs goes first, so a Programs failure changes nothing.
    • If core then rolls back, the enrollments stay ended or withdrawn until the admin retries, and the retry finishes it, because both calls are idempotent.
    • Calling after commit would leave a moved or deactivated resident with active enrollments, which a retry can't fix, unless we add an outbox.
  • The server won't start without PROGRAMS_TOKEN. It has to be in core-secrets before this deploys.

Merge order (nothing enforces it):

  1. unlocked-contracts#8: merged and tagged v0.5.0.
  2. The unlocked-programs PR, with its image in ECR.
  3. The unlocked-platform PR, which turns staging on.
  4. This PR, right after the platform PR deploys to staging.

If any core features or components were removed: none for users. Programs runs as its own service.

Two notes:

  • Swap in real PR numbers for "the unlocked-programs PR" and "the unlocked-platform PR" once they exist.
  • Keep the core PR as a draft, or at least unmerged, until I've run make dev.

@carddev81
carddev81 requested a review from a team as a code owner November 6, 2026 15:34
@carddev81
carddev81 requested review from corypride and removed request for a team November 6, 2026 15:34
@coderabbitai

coderabbitai Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 281 files, which is 181 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: b3ec0bb8-0dbb-4aff-99a7-ccb56ddb91be
Commits

Reviewing files that changed from the base of the PR and between c6bf2b1 and 3bf0f88.

Files ignored due to path filters: 4

  • backend/go.sum is excluded by !**/*.sum
  • backend/migrations/go.sum is excluded by !**/*.sum
  • backend/seeder/go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
Files selected for processing: 281

  • .env.example
  • Makefile
  • README.md
  • backend/.golangci.yml
  • backend/cmd/main.go
  • backend/go.mod
  • backend/migrations/00058_drop_daily_program_tables.sql
  • backend/migrations/go.mod
  • backend/seeder/go.mod
  • backend/seeder/main.go
  • backend/src/database/users.go
  • backend/src/handlers/facilities_handler.go
  • backend/src/handlers/internal_features.go
  • backend/src/handlers/modules.go
  • backend/src/handlers/modules_test.go
  • backend/src/handlers/server.go
  • backend/src/handlers/user_handler.go
  • backend/src/jasper/jasper_service.go
  • backend/src/modules/learningrecord/README.md
  • backend/src/modules/module/module.go
  • backend/src/modules/programs/README.md
  • backend/src/modules/programs/api.go
  • backend/src/modules/programs/api/api.go
  • backend/src/modules/programs/calendar.go
  • backend/src/modules/programs/calendar_store_test.go
  • backend/src/modules/programs/canvas_classes_cache.go
  • backend/src/modules/programs/canvas_classes_cache_test.go
  • backend/src/modules/programs/canvas_events_cache.go
  • backend/src/modules/programs/canvas_programs.go
  • backend/src/modules/programs/canvas_programs_test.go
  • backend/src/modules/programs/change_log_entry.go
  • backend/src/modules/programs/class_enrollments.go
  • backend/src/modules/programs/class_enrollments_store.go
  • backend/src/modules/programs/class_enrollments_test.go
  • backend/src/modules/programs/class_event.go
  • backend/src/modules/programs/class_events.go
  • backend/src/modules/programs/class_events_store.go
  • backend/src/modules/programs/classes_handler.go
  • backend/src/modules/programs/classes_service.go
  • backend/src/modules/programs/dashboard_store.go
  • backend/src/modules/programs/delete_guards_store.go
  • backend/src/modules/programs/essential_ed_programs.go
  • backend/src/modules/programs/essential_ed_programs_test.go
  • backend/src/modules/programs/events_attendance_handler.go
  • backend/src/modules/programs/events_attendance_store.go
  • backend/src/modules/programs/facilities_handler.go
  • backend/src/modules/programs/handlers.go
  • backend/src/modules/programs/helpers_test.go
  • backend/src/modules/programs/http.go
  • backend/src/modules/programs/instructor_conflicts_store.go
  • backend/src/modules/programs/jobs.go
  • backend/src/modules/programs/jobs_test.go
  • backend/src/modules/programs/live_provider.go
  • backend/src/modules/programs/migrations/00001_move_into_programs_schema.sql
  • backend/src/modules/programs/migrations/README.md
  • backend/src/modules/programs/models.go
  • backend/src/modules/programs/module.go
  • backend/src/modules/programs/program.go
  • backend/src/modules/programs/program_class_tier_handler.go
  • backend/src/modules/programs/program_classes.go
  • backend/src/modules/programs/program_classes_store.go
  • backend/src/modules/programs/program_classes_test.go
  • backend/src/modules/programs/programs_handler.go
  • backend/src/modules/programs/programs_insights_store.go
  • backend/src/modules/programs/programs_store.go
  • backend/src/modules/programs/reports.go
  • backend/src/modules/programs/reports_handler.go
  • backend/src/modules/programs/reports_handler_test.go
  • backend/src/modules/programs/reports_store.go
  • backend/src/modules/programs/rooms_store.go
  • backend/src/modules/programs/store.go
  • backend/src/modules/programs/users_handler.go
  • backend/src/modules/programs/users_service.go
  • backend/src/modules/programs/users_store.go
  • backend/src/modules/programs/utils.go
  • backend/src/modules/registry/registry.go
  • backend/src/modules/registry/registry_test.go
  • backend/src/templates/attendance_report.jasper
  • backend/src/templates/attendance_report.jrxml
  • backend/src/templates/class_roster_report.jasper
  • backend/src/templates/class_roster_report.jrxml
  • backend/src/templates/facility_comparison_report.jasper
  • backend/src/templates/facility_comparison_report.jrxml
  • backend/src/templates/program_outcomes_report.jasper
  • backend/src/templates/program_outcomes_report.jrxml
  • backend/src/templates/resident_profile_report.jasper
  • backend/src/templates/resident_profile_report.jrxml
  • backend/tests/integration/attendance_reason_test.go
  • backend/tests/integration/attendance_validation_test.go
  • backend/tests/integration/audit_tracking_test.go
  • backend/tests/integration/class_date_synchronization_test.go
  • backend/tests/integration/class_delete_cascade_test.go
  • backend/tests/integration/class_delete_guard_test.go
  • backend/tests/integration/class_enrollments_test.go
  • backend/tests/integration/class_events_multirow_test.go
  • backend/tests/integration/class_instructor_preload_test.go
  • backend/tests/integration/classes_test.go
  • backend/tests/integration/conflict_detection_test.go
  • backend/tests/integration/enrollment_class_id_test.go
  • backend/tests/integration/event_instructor_change_test.go
  • backend/tests/integration/facility_delete_guard_test.go
  • backend/tests/integration/facility_feature_flags_test.go
  • backend/tests/integration/instructor_conflict_test.go
  • backend/tests/integration/program_completion_flag_test.go
  • backend/tests/integration/program_delete_guard_test.go
  • backend/tests/integration/program_insights_engagement_test.go
  • backend/tests/integration/program_insights_enrollment_by_type_test.go
  • backend/tests/integration/program_insights_load_test.go
  • backend/tests/integration/program_insights_matrix_test.go
  • backend/tests/integration/program_insights_second_enrollment_test.go
  • backend/tests/integration/program_outcomes_report_test.go
  • backend/tests/integration/programs_core_reads_test.go
  • backend/tests/integration/programs_core_routes_test.go
  • backend/tests/integration/programs_fake.go
  • backend/tests/integration/programs_handler_test.go
  • backend/tests/integration/programs_routes_test.go
  • backend/tests/integration/programs_stats_test.go
  • backend/tests/integration/reports_database_test.go
  • backend/tests/integration/reports_test.go
  • backend/tests/integration/resident_programs_feature_test.go
  • backend/tests/integration/room_conflict_test.go
  • backend/tests/integration/rrule_until_integration_test.go
  • backend/tests/integration/testdata/programs/admin_calendar.golden
  • backend/tests/integration/testdata/programs/admin_calendar_bad_cohort.golden
  • backend/tests/integration/testdata/programs/admin_calendar_cohort.golden
  • backend/tests/integration/testdata/programs/attendance_add.golden
  • backend/tests/integration/testdata/programs/attendance_add_cancelled.golden
  • backend/tests/integration/testdata/programs/attendance_add_future.golden
  • backend/tests/integration/testdata/programs/attendance_delete.golden
  • backend/tests/integration/testdata/programs/attendance_delete_not_enrolled.golden
  • backend/tests/integration/testdata/programs/bulk_cancel_nothing.golden
  • backend/tests/integration/testdata/programs/bulk_cancel_short_reason.golden
  • backend/tests/integration/testdata/programs/canvas_schedule.golden
  • backend/tests/integration/testdata/programs/canvas_schedule_not_canvas.golden
  • backend/tests/integration/testdata/programs/class_by_id.golden
  • backend/tests/integration/testdata/programs/class_by_id_missing.golden
  • backend/tests/integration/testdata/programs/class_create.golden
  • backend/tests/integration/testdata/programs/class_create_no_program.golden
  • backend/tests/integration/testdata/programs/class_update.golden
  • backend/tests/integration/testdata/programs/class_update_cohort_field.golden
  • backend/tests/integration/testdata/programs/classes.golden
  • backend/tests/integration/testdata/programs/classes_bad_program.golden
  • backend/tests/integration/testdata/programs/classes_for_program.golden
  • backend/tests/integration/testdata/programs/cohort_attendance_flags.golden
  • backend/tests/integration/testdata/programs/cohort_attendance_rate.golden
  • backend/tests/integration/testdata/programs/cohort_attendance_rate_canvas.golden
  • backend/tests/integration/testdata/programs/cohort_by_id.golden
  • backend/tests/integration/testdata/programs/cohort_by_id_missing.golden
  • backend/tests/integration/testdata/programs/cohort_canvas.golden
  • backend/tests/integration/testdata/programs/cohort_create.golden
  • backend/tests/integration/testdata/programs/cohort_create_no_instructor.golden
  • backend/tests/integration/testdata/programs/cohort_create_room_conflict.golden
  • backend/tests/integration/testdata/programs/cohort_delete.golden
  • backend/tests/integration/testdata/programs/cohort_delete_blocked.golden
  • backend/tests/integration/testdata/programs/cohort_delete_canvas.golden
  • backend/tests/integration/testdata/programs/cohort_delete_check.golden
  • backend/tests/integration/testdata/programs/cohort_delete_check_blocked.golden
  • backend/tests/integration/testdata/programs/cohort_events.golden
  • backend/tests/integration/testdata/programs/cohort_events_after_changes.golden
  • backend/tests/integration/testdata/programs/cohort_events_canvas.golden
  • backend/tests/integration/testdata/programs/cohort_history.golden
  • backend/tests/integration/testdata/programs/cohort_history_schedule.golden
  • backend/tests/integration/testdata/programs/cohort_missing_attendance.golden
  • backend/tests/integration/testdata/programs/cohort_outcomes.golden
  • backend/tests/integration/testdata/programs/cohort_update.golden
  • backend/tests/integration/testdata/programs/cohort_update_terminal.golden
  • backend/tests/integration/testdata/programs/cohorts_update.golden
  • backend/tests/integration/testdata/programs/enroll.golden
  • backend/tests/integration/testdata/programs/enroll_missing_cohort.golden
  • backend/tests/integration/testdata/programs/enrollment_attendance.golden
  • backend/tests/integration/testdata/programs/enrollment_attendance_after_changes.golden
  • backend/tests/integration/testdata/programs/enrollment_conflicts.golden
  • backend/tests/integration/testdata/programs/enrollment_conflicts_none.golden
  • backend/tests/integration/testdata/programs/enrollment_date.golden
  • backend/tests/integration/testdata/programs/enrollment_date_bad.golden
  • backend/tests/integration/testdata/programs/enrollments.golden
  • backend/tests/integration/testdata/programs/enrollments_after_changes.golden
  • backend/tests/integration/testdata/programs/enrollments_after_deactivate.golden
  • backend/tests/integration/testdata/programs/enrollments_by_status.golden
  • backend/tests/integration/testdata/programs/enrollments_delete.golden
  • backend/tests/integration/testdata/programs/enrollments_update.golden
  • backend/tests/integration/testdata/programs/enrollments_update_invalid.golden
  • backend/tests/integration/testdata/programs/event_attendance.golden
  • backend/tests/integration/testdata/programs/event_attendance_cancelled.golden
  • backend/tests/integration/testdata/programs/event_attendance_rate.golden
  • backend/tests/integration/testdata/programs/event_create.golden
  • backend/tests/integration/testdata/programs/event_create_no_rule.golden
  • backend/tests/integration/testdata/programs/event_override_delete.golden
  • backend/tests/integration/testdata/programs/event_override_patch.golden
  • backend/tests/integration/testdata/programs/event_override_patch_no_date.golden
  • backend/tests/integration/testdata/programs/event_override_uncancel.golden
  • backend/tests/integration/testdata/programs/event_overrides.golden
  • backend/tests/integration/testdata/programs/event_series_reschedule.golden
  • backend/tests/integration/testdata/programs/event_uncancel_series.golden
  • backend/tests/integration/testdata/programs/event_uncancel_series_no_date.golden
  • backend/tests/integration/testdata/programs/facilities.golden
  • backend/tests/integration/testdata/programs/facility_delete_check.golden
  • backend/tests/integration/testdata/programs/facility_instructors.golden
  • backend/tests/integration/testdata/programs/historical_enrollment.golden
  • backend/tests/integration/testdata/programs/historical_enrollment_no_dates.golden
  • backend/tests/integration/testdata/programs/instructor_classes.golden
  • backend/tests/integration/testdata/programs/instructor_classes_no_dates.golden
  • backend/tests/integration/testdata/programs/missing_attendance.golden
  • backend/tests/integration/testdata/programs/program_archive_check.golden
  • backend/tests/integration/testdata/programs/program_by_id.golden
  • backend/tests/integration/testdata/programs/program_by_id_bad_id.golden
  • backend/tests/integration/testdata/programs/program_by_id_missing.golden
  • backend/tests/integration/testdata/programs/program_canvas.golden
  • backend/tests/integration/testdata/programs/program_classes.golden
  • backend/tests/integration/testdata/programs/program_classes_after_deletes.golden
  • backend/tests/integration/testdata/programs/program_classes_all_facilities.golden
  • backend/tests/integration/testdata/programs/program_cohorts.golden
  • backend/tests/integration/testdata/programs/program_cohorts_canvas.golden
  • backend/tests/integration/testdata/programs/program_cohorts_none.golden
  • backend/tests/integration/testdata/programs/program_completions.golden
  • backend/tests/integration/testdata/programs/program_completions_self.golden
  • backend/tests/integration/testdata/programs/program_create.golden
  • backend/tests/integration/testdata/programs/program_create_bad_body.golden
  • backend/tests/integration/testdata/programs/program_create_duplicate.golden
  • backend/tests/integration/testdata/programs/program_create_missing_name.golden
  • backend/tests/integration/testdata/programs/program_delete.golden
  • backend/tests/integration/testdata/programs/program_delete_blocked.golden
  • backend/tests/integration/testdata/programs/program_delete_check.golden
  • backend/tests/integration/testdata/programs/program_filters.golden
  • backend/tests/integration/testdata/programs/program_history.golden
  • backend/tests/integration/testdata/programs/program_history_status.golden
  • backend/tests/integration/testdata/programs/program_status_archive_blocked.golden
  • backend/tests/integration/testdata/programs/program_status_inactive.golden
  • backend/tests/integration/testdata/programs/program_status_missing.golden
  • backend/tests/integration/testdata/programs/program_update.golden
  • backend/tests/integration/testdata/programs/program_update_missing.golden
  • backend/tests/integration/testdata/programs/programs.golden
  • backend/tests/integration/testdata/programs/programs_after_changes.golden
  • backend/tests/integration/testdata/programs/programs_csv.golden
  • backend/tests/integration/testdata/programs/programs_detailed_list.golden
  • backend/tests/integration/testdata/programs/programs_paged.golden
  • backend/tests/integration/testdata/programs/programs_search.golden
  • backend/tests/integration/testdata/programs/programs_stats_all.golden
  • backend/tests/integration/testdata/programs/programs_stats_facility.golden
  • backend/tests/integration/testdata/programs/report_bad_type.golden
  • backend/tests/integration/testdata/programs/report_class_roster.golden
  • backend/tests/integration/testdata/programs/report_comparison_facility_admin.golden
  • backend/tests/integration/testdata/programs/report_program_outcomes.golden
  • backend/tests/integration/testdata/programs/report_resident_profile.golden
  • backend/tests/integration/testdata/programs/report_roster_no_cohort.golden
  • backend/tests/integration/testdata/programs/resident_verify.golden
  • backend/tests/integration/testdata/programs/room_create.golden
  • backend/tests/integration/testdata/programs/room_create_no_name.golden
  • backend/tests/integration/testdata/programs/rooms.golden
  • backend/tests/integration/testdata/programs/student_calendar.golden
  • backend/tests/integration/testdata/programs/student_calendar_bad_range.golden
  • backend/tests/integration/testdata/programs/todays_schedule.golden
  • backend/tests/integration/testdata/programs/user_attendance_export.golden
  • backend/tests/integration/testdata/programs/user_attendance_trend.golden
  • backend/tests/integration/testdata/programs/user_programs.golden
  • backend/tests/integration/testdata/programs_core_reads/enrollments_after_transfer.golden
  • backend/tests/integration/testdata/programs_core_reads/welding_enrollments_after_transfer.golden
  • backend/tests/integration/testdata/programs_core_routes/account_history_after_deactivate.golden
  • backend/tests/integration/testdata/programs_core_routes/account_history_attendance.golden
  • backend/tests/integration/testdata/programs_core_routes/dashboard_class_metrics.golden
  • backend/tests/integration/testdata/programs_core_routes/dashboard_facility_health.golden
  • backend/tests/integration/testdata/programs_core_routes/metrics_completion_matrix.golden
  • backend/tests/integration/testdata/programs_core_routes/metrics_engagement.golden
  • backend/tests/integration/testdata/programs_core_routes/metrics_enrollment_by_type.golden
  • backend/tests/integration/testdata/programs_core_routes/metrics_load_distribution.golden
  • backend/tests/integration/testdata/programs_core_routes/metrics_second_enrollment.golden
  • backend/tests/integration/testdata/programs_core_routes/user_deactivate.golden
  • backend/tests/integration/testdata/programs_core_routes/users_bulk_deactivate.golden
  • backend/tests/integration/testdata/programs_core_routes/users_include_bad_cohort.golden
  • backend/tests/integration/testdata/programs_core_routes/users_only_enrolled.golden
  • backend/tests/integration/testdata/programs_core_routes/users_only_unenrolled.golden
  • backend/tests/integration/testenv.go
  • backend/tests/integration/user_attendance_export_test.go
  • config/dev.nginx.conf
  • docker-compose.yml
  • frontend/src/components/schedule/BulkCancelClassesModal.tsx
  • frontend/src/components/schedule/ChangeInstructorModal.tsx
  • frontend/src/pages/admin/ResidentProfile.tsx
  • frontend/src/pages/admin/resident-profile/DetailedAttendanceDialog.tsx
  • frontend/src/pages/learning/ResidentOverview.tsx
  • frontend/src/pages/programs/ClassManagementForm.tsx

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@corypride corypride left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@carddev81 I ran this locally with platform #21 and programs #1. Migrations, routing, transfer, deactivate, metrics and all 5 PDF reports work. With Programs down, nothing changes.

  1. A transfer that keeps failing still ends the enrollments. (Low) Programs is called before the target facility is checked (user_handler.go:619). Transferring to a facility that doesn't exist returns 500 every time, but the enrollment is already Incomplete: Transferred. Please validate trans_facility_id before calling Programs.
  2. Merge order. (Medium) The server won't start without PROGRAMS_TOKEN (main.go:59-61), so this has to merge after platform #21's staging commit and the token sync.
  3. Programs still writes user_account_history. (Low) EN-207 rules that out, so it needs a sign-off or a ticket.

Will approve once finding 1 is fixed. Merge after programs #1 and platform #21.

@carddev81

Copy link
Copy Markdown
Contributor Author

@carddev81 I ran this locally with platform #21 and programs #1. Migrations, routing, transfer, deactivate, metrics and all 5 PDF reports work. With Programs down, nothing changes.

  1. A transfer that keeps failing still ends the enrollments. (Low) Programs is called before the target facility is checked (user_handler.go:619). Transferring to a facility that doesn't exist returns 500 every time, but the enrollment is already Incomplete: Transferred. Please validate trans_facility_id before calling Programs.
  2. Merge order. (Medium) The server won't start without PROGRAMS_TOKEN (main.go:59-61), so this has to merge after platform feat: add favicon logo for browser tab #21's staging commit and the token sync.
  3. Programs still writes user_account_history. (Low) EN-207 rules that out, so it needs a sign-off or a ticket.

Will approve once finding 1 is fixed. Merge after programs #1 and platform #21.

Thanks for running it end to end.

  1. Fixed in : the transfer checks trans_facility_id exists before calling Programs, so an unknown facility now returns 400 facilities not found and no enrollment changes. TestTransferToUnknownFacility pins it: Programs is set to fail, so the test would get a 500 if the handler reached Programs.

  2. Merge order: agreed, and it's the plan in the description:

    1. Programs feat(ci): add testing + conventional commit workflow #1 merges first.
    2. PROGRAMS_TOKEN goes into staging's core-secrets.
    3. platform feat: add favicon logo for browser tab #21 merges with its staging commit.
    4. This PR merges last, right after feat: add favicon logo for browser tab #21 deploys.
  3. user_account_history: this was signed off as the one exception when EN-207 was planned. Programs can only read and insert (never update or delete), so recording attendance writes its history row in the same transaction as the attendance. It's documented in unlocked-programs' README under the database role's grants. Everything else of core's is read-only for Programs, and Postgres enforces it.

@carddev81
carddev81 requested a review from corypride November 6, 2026 17:55
corypride
corypride previously approved these changes Nov 6, 2026

@corypride corypride left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@carddev81 Re-reviewed at 3fa9d60. Fix 1 is good: an unknown facility now gets a 400 from GetFacilityByID before Programs is called, TestTransferToUnknownFacility pins it, and CI is green. Your answers on 2 and 3 work for me.

One new thing: f6bf432 (“drop ticket numbers from code comments”) also uncommented PROGRAMS_DIR=../unlocked-programs in .env.example:68. Anyone who copies .env.example without that sibling checkout now gets make dev stopping with “there’s no checkout there”. Open Content and Provider Platforms leave theirs commented, so please comment it back out.

Approving, but make sure that line is commented out. Merge order as you listed it.

@carddev81
carddev81 merged commit fa45ccf into main Nov 7, 2026
11 checks passed
@carddev81
carddev81 deleted the EN-207-move-programs-to-its-own-repo branch November 7, 2026 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants